//
//  SimpleArchiveManager.swift
//  ZipTool
//
//  Created by jerry on 2025/8/15.
//

import Foundation
import SWCompression

struct SimpleArchiveEntry {
    let name: String
    let size: Int64
    let isDirectory: Bool
    let modificationDate: Date?
}

class SimpleArchiveManager: ObservableObject {
    @Published var entries: [SimpleArchiveEntry] = []
    @Published var isLoading = false
    @Published var errorMessage: String?
    
    private var currentArchiveURL: URL?
    private var currentArchiveData: Data?
    
    func loadArchive(from url: URL) {
        currentArchiveURL = url
        isLoading = true
        errorMessage = nil
        entries = []
        
        DispatchQueue.global(qos: .userInitiated).async {
            do {
                let data = try Data(contentsOf: url)
                self.currentArchiveData = data
                
                let archiveEntries = try self.extractEntriesFromZip(data: data)
                
                DispatchQueue.main.async {
                    self.entries = archiveEntries
                    self.isLoading = false
                }
            } catch {
                DispatchQueue.main.async {
                    self.errorMessage = "读取压缩包失败: \(error.localizedDescription)"
                    self.isLoading = false
                }
            }
        }
    }
    
    private func extractEntriesFromZip(data: Data) throws -> [SimpleArchiveEntry] {
        let zipEntries = try ZipContainer.open(container: data)
        var entries: [SimpleArchiveEntry] = []
        
        for entry in zipEntries {
            entries.append(SimpleArchiveEntry(
                name: entry.info.name,
                size: Int64(entry.data?.count ?? 0),
                isDirectory: entry.info.name.hasSuffix("/"),
                modificationDate: entry.info.modificationTime
            ))
        }
        
        return entries.sorted { $0.name < $1.name }
    }
    
    func extractArchive(to destinationURL: URL, progressHandler: @escaping (Double) -> Void) {
        guard let archiveData = currentArchiveData else {
            errorMessage = "没有加载压缩包数据"
            return
        }
        
        isLoading = true
        errorMessage = nil
        
        DispatchQueue.global(qos: .userInitiated).async {
            do {
                try self.performZipExtraction(
                    data: archiveData,
                    to: destinationURL,
                    progressHandler: progressHandler
                )
                
                DispatchQueue.main.async {
                    self.isLoading = false
                }
            } catch {
                DispatchQueue.main.async {
                    self.errorMessage = "解压失败: \(error.localizedDescription)"
                    self.isLoading = false
                }
            }
        }
    }
    
    private func performZipExtraction(
        data: Data,
        to destinationURL: URL,
        progressHandler: @escaping (Double) -> Void
    ) throws {
        let zipEntries = try ZipContainer.open(container: data)
        let fileManager = FileManager.default
        let totalEntries = zipEntries.count
        
        // 获取安全访问权限
        let hasAccess = destinationURL.startAccessingSecurityScopedResource()
        defer {
            if hasAccess {
                destinationURL.stopAccessingSecurityScopedResource()
            }
        }
        
        // 创建目标目录
        try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true)
        
        for (index, entry) in zipEntries.enumerated() {
            let entryURL = destinationURL.appendingPathComponent(entry.info.name)
            
            if entry.info.name.hasSuffix("/") {
                // 目录
                try fileManager.createDirectory(at: entryURL, withIntermediateDirectories: true)
            } else {
                // 文件
                let parentURL = entryURL.deletingLastPathComponent()
                try fileManager.createDirectory(at: parentURL, withIntermediateDirectories: true)
                
                if let entryData = entry.data {
                    try entryData.write(to: entryURL, options: .atomic)
                }
            }
            
            DispatchQueue.main.async {
                progressHandler(Double(index + 1) / Double(totalEntries))
            }
        }
    }
    
    /// 从已加载的压缩包中提取单个条目到临时目录，完成后返回文件 URL（目录或不存在的条目返回 nil）
    func extractEntry(named name: String, completion: @escaping (URL?) -> Void) {
        guard let archiveData = currentArchiveData else {
            DispatchQueue.main.async { completion(nil) }
            return
        }
        
        DispatchQueue.global(qos: .userInitiated).async {
            do {
                let zipEntries = try ZipContainer.open(container: archiveData)
                guard let entry = zipEntries.first(where: { $0.info.name == name }) else {
                    DispatchQueue.main.async { completion(nil) }
                    return
                }
                
                // 如果是目录或没有数据，则返回 nil
                if entry.info.name.hasSuffix("/") || entry.data == nil {
                    DispatchQueue.main.async { completion(nil) }
                    return
                }
                
                let entryData = entry.data!
                let tempDir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
                let uniqueName = UUID().uuidString + "-" + (entry.info.name as NSString).lastPathComponent
                let tempURL = tempDir.appendingPathComponent(uniqueName)
                
                try entryData.write(to: tempURL, options: .atomic)
                
                if let modDate = entry.info.modificationTime {
                    try FileManager.default.setAttributes([.modificationDate: modDate], ofItemAtPath: tempURL.path)
                }
                
                DispatchQueue.main.async {
                    completion(tempURL)
                }
            } catch {
                DispatchQueue.main.async {
                    completion(nil)
                }
            }
        }
    }
}
